package com.cc.dlnalib

import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.cybergarage.upnp.ControlPoint
import org.cybergarage.upnp.Device
import org.cybergarage.upnp.device.DeviceChangeListener
import java.util.concurrent.Executors
import kotlin.concurrent.thread

private const val WAIT_INTERVAL = 500L;

class DeviceFinder(private val controlPoint: ControlPoint) {
    val tag = "DeviceFinder"
    private val deviceList = mutableListOf<Device>()
    private var isStart = false
    val executor = Executors.newCachedThreadPool()

    @Volatile
    private var isSearching = false
    private val callbacks = mutableListOf<(MutableList<Device>) -> Unit>()

    init {
        this.controlPoint.addDeviceChangeListener(object : DeviceChangeListener {
            override fun deviceRemoved(p0: Device?) {
                if (!isMediaRenderDevice(p0)) {
                    return
                }
                Log.i(tag, "deviceRemoved:${p0?.friendlyName}")
                deviceList.removeAll { p0?.udn == it.udn }
                synchronized(lock) {
                    GlobalScope.launch(Dispatchers.Main) {
                        callbacks.forEach { it.invoke(deviceList) }
                    }
                }
            }

            override fun deviceAdded(p0: Device?) {
                if (!isMediaRenderDevice(p0)) {
                    return
                }
                Log.i(tag, "deviceAdded:${p0?.friendlyName}")
                if (deviceList.firstOrNull { p0?.udn == it.udn } == null) {
                    p0?.let { deviceList.add(it) }
                }
                synchronized(lock) {
                    GlobalScope.launch(Dispatchers.Main) {
                        callbacks.forEach { it.invoke(deviceList) }
                    }
                }
            }
        })
    }

    val lock = java.lang.Object()
    fun search() {
        if (deviceList.isNotEmpty()) {
            GlobalScope.launch(Dispatchers.Main) {
                callbacks.forEach { it.invoke(deviceList) }
            }
        }
        if (isStart) {
            if (isSearching) {
                return;
            }
            executor.submit {
                kotlin.runCatching {
                    while (isSearching) {
                        synchronized(lock) {
                            controlPoint.search()
                            lock.wait(WAIT_INTERVAL)
                        }
                    }
                }
            }
        } else {
            executor.submit {
                kotlin.runCatching {
                    controlPoint.start()
                }
            }
        }
        isSearching = true;
    }

    fun stopSearch() {
        isSearching = false
    }

    var MEDIARENDER = "urn:schemas-upnp-org:device:MediaRenderer:1"

    /**
     * Check if the device is a media render device
     *
     * @param device
     * @return
     */
    fun isMediaRenderDevice(device: Device?): Boolean {
        return (device != null
                && MEDIARENDER.equals(device.deviceType, ignoreCase = true))
    }

    fun addCallBack(block: (MutableList<Device>) -> Unit) {
        synchronized(lock) {
            if (!callbacks.contains(block)) {
                callbacks.add(block)
            }
        }
    }

    fun removeCallBack(block: (MutableList<Device>) -> Unit) {
        synchronized(lock) {
            callbacks.remove(block)
        }
    }
}